/******************************************************************************

    Simple Authentication and Authorisation System (SAAS)

    pkt-user.c

    Copyright (C) 2011  Josh Goes


    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

    If you require a mailing address for this software, please send your
    mail to the following address:
        SAAS
        GPO Box 2973
        BRISBANE QLD 4001
        AUSTRALIA

******************************************************************************/



#include "saas-build.h"

#include <string.h>
#include <stdlib.h>



/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////
////  REQUESTS
////

uint16_t pkt_req_uid_to_username( saas_t *inst , uint32_t *trans_id , uint8_t *pkt , uint16_t *pkt_len , uint32_t uid )
{
	uint8_t  payload[SAAS_MAX_PKT_LEN];
	uint32_t  *ptr = (uint32_t *) &payload;

	// set the payload
	memset( payload , 0 , sizeof( payload ) );
	*ptr = u32tonet( uid );

	// now we call pkt_pack to pack it altogether into a single packet.
	pkt_req( inst , trans_id , pkt , pkt_len , SAAS_MAGIC_REQ , SAAS_PKT_TYPE_UID_TO_USERNAME , payload , 4 );

	return SAAS_OP_SUCCESS;
}



uint16_t pkt_req_username_to_uid( saas_t *inst , uint32_t *trans_id , uint8_t *pkt , uint16_t *pkt_len , const uint8_t *username , uint8_t username_len )
{
	uint8_t  payload[SAAS_MAX_PKT_LEN];

	// set the payload
	memset( payload , 0 , sizeof( payload ) );
	payload[0] = username_len;
	memcpy( payload + 1 , username , username_len );

	// now we call pkt_pack to pack it altogether into a single packet.
	pkt_req( inst , trans_id , pkt , pkt_len , SAAS_MAGIC_REQ , SAAS_PKT_TYPE_USERNAME_TO_UID , payload , username_len + 1 );

	return SAAS_OP_SUCCESS;
}


uint16_t pkt_req_user_info_by_uid( saas_t *inst , uint32_t *trans_id , uint8_t *pkt , uint16_t *pkt_len , uint32_t uid )
{
	uint8_t  payload[SAAS_MAX_PKT_LEN];
	uint32_t  *ptr = (uint32_t *) &payload;

	// set the payload
	memset( payload , 0 , sizeof( payload ) );
	*ptr = u32tonet( uid );

	// now we call pkt_pack to pack it altogether into a single packet.
	pkt_req( inst , trans_id , pkt , pkt_len , SAAS_MAGIC_REQ , SAAS_PKT_TYPE_USER_INFO_BY_UID , payload , 4 );

	return SAAS_OP_SUCCESS;
}


uint16_t pkt_req_user_info_by_username( saas_t *inst , uint32_t *trans_id , uint8_t *pkt , uint16_t *pkt_len , const uint8_t *username , uint8_t username_len )
{
	uint8_t  payload[SAAS_MAX_PKT_LEN];

	// set the payload
	memset( payload , 0 , sizeof( payload ) );
	payload[0] = username_len;
	memcpy( payload + 1 , username , username_len );

	// now we call pkt_pack to pack it altogether into a single packet.
	pkt_req( inst , trans_id , pkt , pkt_len , SAAS_MAGIC_REQ , SAAS_PKT_TYPE_USER_INFO_BY_USERNAME , payload , username_len + 1 );

	return SAAS_OP_SUCCESS;
}


uint16_t pkt_req_user_info_with_groups_by_uid( saas_t *inst , uint32_t *trans_id , uint8_t *pkt , uint16_t *pkt_len , uint32_t uid )
{
	uint8_t  payload[SAAS_MAX_PKT_LEN];
	uint32_t  *ptr = (uint32_t *) &payload;

	// set the payload
	memset( payload , 0 , sizeof( payload ) );
	*ptr = u32tonet( uid );

	// now we call pkt_pack to pack it altogether into a single packet.
	pkt_req( inst , trans_id , pkt , pkt_len , SAAS_MAGIC_REQ , SAAS_PKT_TYPE_USER_INFO_WITH_GROUPS_BY_UID , payload , 4 );

	return SAAS_OP_SUCCESS;
}


uint16_t pkt_req_user_info_with_groups_by_username( saas_t *inst , uint32_t *trans_id , uint8_t *pkt , uint16_t *pkt_len , const uint8_t *username , uint8_t username_len )
{
	uint8_t  payload[SAAS_MAX_PKT_LEN];

	// set the payload
	memset( payload , 0 , sizeof( payload ) );
	payload[0] = username_len;
	memcpy( payload + 1 , username , username_len );

	// now we call pkt_pack to pack it altogether into a single packet.
	pkt_req( inst , trans_id , pkt , pkt_len , SAAS_MAGIC_REQ , SAAS_PKT_TYPE_USER_INFO_WITH_GROUPS_BY_USERNAME , payload , username_len + 1 );

	return SAAS_OP_SUCCESS;
}


uint16_t pkt_req_user_info_with_all_groups_by_uid( saas_t *inst , uint32_t *trans_id , uint8_t *pkt , uint16_t *pkt_len , uint32_t uid )
{
	uint8_t  payload[SAAS_MAX_PKT_LEN];
	uint32_t  *ptr = (uint32_t *) &payload;

	// set the payload
	memset( payload , 0 , sizeof( payload ) );
	*ptr = u32tonet( uid );

	// now we call pkt_pack to pack it altogether into a single packet.
	pkt_req( inst , trans_id , pkt , pkt_len , SAAS_MAGIC_REQ , SAAS_PKT_TYPE_USER_INFO_WITH_ALL_GROUPS_BY_UID , payload , 4 );

	return SAAS_OP_SUCCESS;
}


uint16_t pkt_req_user_info_with_all_groups_by_username( saas_t *inst , uint32_t *trans_id , uint8_t *pkt , uint16_t *pkt_len , const uint8_t *username , uint8_t username_len )
{
	uint8_t  payload[SAAS_MAX_PKT_LEN];

	// set the payload
	memset( payload , 0 , sizeof( payload ) );
	payload[0] = username_len;
	memcpy( payload + 1 , username , username_len );

	// now we call pkt_pack to pack it altogether into a single packet.
	pkt_req( inst , trans_id , pkt , pkt_len , SAAS_MAGIC_REQ , SAAS_PKT_TYPE_USER_INFO_WITH_ALL_GROUPS_BY_USERNAME , payload , username_len + 1 );

	return SAAS_OP_SUCCESS;
}





/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////
////  RESPONSES
////


uint16_t pkt_resp_uid_to_username( saas_t *inst , uint32_t trans_id , uint8_t *pkt , uint16_t pkt_len , uint8_t **username )
{
	uint16_t ret = pkt_resp( inst , trans_id , pkt , pkt_len );
	if ( ret != SAAS_OP_SUCCESS )
		return ret;

	pkt_hdr_t *hdr = (pkt_hdr_t *) pkt;
	if ( hdr->type == SAAS_PKT_TYPE_UID_TO_USERNAME && hdr->payload_len >= 2 )
	{
		// the right kind of response we're looking for.
		uint16_t *fret         = (uint16_t *)  pkt + ( sizeof( pkt_hdr_t ) / 2 );
		uint8_t  *username_len = (uint8_t *)   pkt + sizeof( pkt_hdr_t ) + 4;
		logm( inst , LOG_DEBUG , "username_len = %d" , *username_len );
		*username = malloc( *username_len + 1 );
		if ( *username == NULL )
			return SAAS_OP_MEM_ERROR;
		memset( *username , 0 , *username_len + 1 );
		memcpy( *username , pkt + sizeof( pkt_hdr_t ) + 5 , *username_len );
		return *fret;
	}

	return SAAS_OP_DECRYPT_ERROR;
}




uint16_t pkt_resp_username_to_uid( saas_t *inst , uint32_t trans_id , uint8_t *pkt , uint16_t pkt_len , uint32_t *uid )
{
	uint16_t ret = pkt_resp( inst , trans_id , pkt , pkt_len );
	if ( ret != SAAS_OP_SUCCESS )
		return ret;

	pkt_hdr_t *hdr = (pkt_hdr_t *) pkt;
	if ( hdr->type == SAAS_PKT_TYPE_USERNAME_TO_UID && hdr->payload_len >= 2 )
	{
		// the right kind of response we're looking for.
		uint16_t  *fret = (uint16_t  *)  pkt + ( sizeof( pkt_hdr_t ) / 2 );
		*uid            = u32fromnet( *( (uint32_t *)  pkt + ( sizeof( pkt_hdr_t ) / 4 ) + 1 ) );
		return *fret;
	}

	return SAAS_OP_DECRYPT_ERROR;
}



uint16_t pkt_resp_user_info( saas_t *inst , uint32_t trans_id , uint8_t *pkt , uint16_t pkt_len , saas_user_info_t **user_info )
{
	uint8_t  *lol;
	saas_user_info_t *ui;
	uint32_t  roffset, woffset, k;
	uint32_t  rsz = 0, sz = 0;

	uint16_t  ret = pkt_resp( inst , trans_id , pkt , pkt_len );
	if ( ret != SAAS_OP_SUCCESS )
		return ret;

	pkt_hdr_t *hdr = (pkt_hdr_t *) pkt;
	switch ( hdr->type )
	{
		case SAAS_PKT_TYPE_USER_INFO_BY_UID:
		case SAAS_PKT_TYPE_USER_INFO_BY_USERNAME:
		case SAAS_PKT_TYPE_USER_INFO_WITH_GROUPS_BY_UID:
		case SAAS_PKT_TYPE_USER_INFO_WITH_GROUPS_BY_USERNAME:
		case SAAS_PKT_TYPE_USER_INFO_WITH_ALL_GROUPS_BY_UID:
		case SAAS_PKT_TYPE_USER_INFO_WITH_ALL_GROUPS_BY_USERNAME:
			break;

		default:
			return SAAS_OP_DECRYPT_ERROR;
	}

	if ( hdr->payload_len >= 2 )
	{
		// first size the malloc to be the same as saas_user_info_t + pkt_len
		// so that the varying lengths of usernames, etc are covered for.
		sz = sizeof( saas_user_info_t ) + pkt_len;
		lol = malloc( sz );
		if ( lol == NULL )
			return SAAS_OP_MEM_ERROR;
		memset( lol , 0 , sz );
		ui = (saas_user_info_t *) lol;

		// set the read offset to start at the start of the payload.
		roffset = sizeof( pkt_hdr_t );
		// and set the write offset to the end of the saas user info struct.
		woffset = sizeof( saas_user_info_t );

		// capture the result of the operation.
		memcpy( &ret  ,  pkt + roffset  ,  2 );  roffset += 2;
		ret = u16fromnet( ret );
		if ( ret != SAAS_OP_SUCCESS )
		{
			free( lol );
			return ret;
		}

		// capture the number of groups in this response.
		memcpy( &ui->num_groups , pkt + roffset , 2 );  roffset += 2;
		ui->num_groups = u16fromnet( ui->num_groups );

		// capture the UID and GID.
		memcpy( &ui->uid , pkt + roffset , 4 );  roffset += 4;
		memcpy( &ui->gid , pkt + roffset , 4 );  roffset += 4;
		ui->uid = u32fromnet( ui->uid );
		ui->gid = u32fromnet( ui->gid );

		// capture the username.
		memcpy( &ui->username_len , pkt + roffset , 1 );  roffset += 1;
		ui->username = (char *) lol + woffset;
		strncpy( ui->username , (char *) pkt + roffset , ui->username_len );  roffset += ui->username_len;
		woffset += ui->username_len + 1;

		// capture the full name.
		memcpy( &ui->fullname_len , pkt + roffset , 1 );  roffset += 1;
		ui->fullname = (char *) lol + woffset;
		strncpy( ui->fullname , (char *) pkt + roffset , ui->fullname_len );  roffset += ui->fullname_len;
		woffset += ui->fullname_len + 1;

		// capture the display name.
		memcpy( &ui->displayname_len , pkt + roffset , 1 );  roffset += 1;
		ui->displayname = (char *) lol + woffset;
		strncpy( ui->displayname , (char *) pkt + roffset , ui->displayname_len );  roffset += ui->displayname_len;
		woffset += ui->displayname_len + 1;

		// capture the first name.
		memcpy( &ui->fname_len , pkt + roffset , 1 );  roffset += 1;
		ui->fname = (char *) lol + woffset;
		strncpy( ui->fname , (char *) pkt + roffset , ui->fname_len );  roffset += ui->fname_len;
		woffset += ui->fname_len + 1;

		// capture the last name.
		memcpy( &ui->lname_len , pkt + roffset , 1 );  roffset += 1;
		ui->lname = (char *) lol + woffset;
		strncpy( ui->lname , (char *) pkt + roffset , ui->lname_len );  roffset += ui->lname_len;
		woffset += ui->lname_len + 1;

		// capture the email address.
		memcpy( &ui->email_len , pkt + roffset , 1 );  roffset += 1;
		ui->email = (char *) lol + woffset;
		strncpy( ui->email , (char *) pkt + roffset , ui->email_len );  roffset += ui->email_len;
		woffset += ui->email_len + 1;

		// capture the home directory.
		memcpy( &ui->home_dir_len , pkt + roffset , 1 );  roffset += 1;
		ui->home_dir = (char *) lol + woffset;
		strncpy( ui->home_dir , (char *) pkt + roffset , ui->home_dir_len );  roffset += ui->home_dir_len;
		woffset += ui->home_dir_len + 1;

		// capture the shell
		memcpy( &ui->shell_len , pkt + roffset , 1 );  roffset += 1;
		ui->shell = (char *) lol + woffset;
		strncpy( ui->shell , (char *) pkt + roffset , ui->shell_len );  roffset += ui->shell_len;
		woffset += ui->shell_len + 1;

		// if there are any groups, load 'em in.
		if ( ui->num_groups > 0 )
		{
			// at this point we'll need to extend our malloc to include
			// all the group info.
			rsz  = sizeof( saas_user_group_t * ) * ( ui->num_groups + 1 );
			rsz += sizeof( saas_user_group_t ) * ui->num_groups;
			// the groupname lengths would have been covered by the pkt_len/sz malloc
			// above.
			lol = realloc( lol , sz + rsz );
			if ( lol == NULL )
				return SAAS_OP_MEM_ERROR;
			ui = ( saas_user_info_t *) lol;
			// blank out the EXTENSION ONLY, not the stuff we've
			// already populated.
			memset( lol + sz , 0 , rsz );

			uint8_t  groupname_len;

			// set up the double-pointer array...
			ui->groups = (saas_user_group_t **) ( (uint8_t *) lol + woffset );
			woffset += sizeof( saas_user_group_t * ) * ( ui->num_groups + 1 );

			// set up room for each group element
			for ( k = 0 ; k < ui->num_groups ; k++ )
			{
				ui->groups[k] = (saas_user_group_t *) ( (uint8_t *) lol + woffset );
				woffset += sizeof( saas_user_group_t );
			}

			for ( k = 0 ; k < ui->num_groups ; k++ )
			{
				// copy the GID.
				ui->groups[k]->gid = *( (uint32_t *) ( (uint8_t *) pkt + roffset ) );   roffset += 4;
				ui->groups[k]->gid = u32fromnet( ui->groups[k]->gid );

				// copy the group name.
				memcpy( &groupname_len , pkt + roffset , 1 );   roffset += 1;
				ui->groups[k]->groupname = (char *) lol + woffset;  woffset += groupname_len + 1;
				strncpy( ui->groups[k]->groupname , (char *) pkt + roffset , groupname_len );  roffset += groupname_len;
			}

		}

		// and finally map the malloc address back to the calling function.
		*user_info = ui;

		return ret;
	}

	return SAAS_OP_DECRYPT_ERROR;
}


